import logging
from typing import Dict, Optional, List, Tuple
import openai
from django.core.cache import cache
from Notebook import settings
from apps.ai.services.ai_service import AIService
from datetime import datetime, timedelta
import hashlib
import json

logger = logging.getLogger('apps')

class AIProcessingError(Exception):
    """AI 处理异常"""
    pass

class RateLimitError(AIProcessingError):
    """请求频率限制错误"""
    pass

class ScheduleAIService:
    """日程AI服务类"""
    
    def __init__(self):
        self.ai_service = AIService()
        self.cache_timeout = 3600  # 缓存1小时
        # 初始化 OpenAI客户端
        self.client = openai.OpenAI(
            api_key=settings.AI_API_KEY,
            base_url=settings.AI_API_BASE
        )
    
    def _get_cache_key(self, prefix: str, content: str) -> str:
        """生成缓存键"""
        content_hash = hashlib.md5(content.encode()).hexdigest()
        return f"schedule_ai:{prefix}:{content_hash}"

    def _get_from_cache(self, key: str) -> Optional[Dict]:
        """从缓存获取数据"""
        try:
            data = cache.get(key)
            return json.loads(data) if data else None
        except Exception:
            return None

    def _save_to_cache(self, key: str, data: Dict) -> bool:
        """保存数据到缓存"""
        try:
            cache.set(key, json.dumps(data), self.cache_timeout)
            return True
        except Exception:
            return False

    def analyze_content(self, content: str) -> Optional[Dict]:
        """分析内容并提取日程信息"""
        try:
            # 检查缓存
            cache_key = self._get_cache_key('analyze', content)
            cached_result = self._get_from_cache(cache_key)
            if cached_result:
                return cached_result

            # 构建更严格的提示词
            prompt = f"""
            请分析以下日程内容，并以严格的JSON格式返回结果。每个日程必须包含所有必需字段。
            对于description字段，请提取3-5个关键词或短语，用逗号分隔。

            内容：{content}

            要求：
            1. 返回一个日程数组，每个日程都是一个完整的JSON对象
            2. 所有日期格式为YYYY-MM-DD
            3. 所有时间格式为HH:MM
            4. 重要程度必须是"high"/"medium"/"low"之一，必须使用双引号
            5. description字段必须是3-5个关键词或短语，用逗号分隔
            6. 所有字符串值必须使用双引号
            7. 确保JSON格式完全正确，不能有多余的逗号或引号
            
            示例格式：
            [
                {{
                    "title": "日程标题",
                    "description": "关键词1,关键词2,关键词3",
                    "date": "YYYY-MM-DD",
                    "start_time": "HH:MM",
                    "end_time": "HH:MM",
                    "location": "地点",
                    "importance": "high",
                    "key_points": [
                        "关键点1",
                        "关键点2"
                    ]
                }}
            ]
            """
            
            # 调用AI服务
            response = self.ai_service.chat_completion(prompt, temperature=0.3)
            if not response:
                return None
            
            # 清理响应文本，确保是有效的JSON
            response = response.strip()
            if response.startswith('```json'):
                response = response[7:]
            if response.endswith('```'):
                response = response[:-3]
            response = response.strip()
            
            # 解析结果
            try:
                result = json.loads(response)
                if not isinstance(result, list):
                    result = [result]
                
                # 验证每个日程的格式
                for schedule in result:
                    if not all(key in schedule for key in ['title', 'description', 'date', 'start_time', 'end_time', 'location', 'importance', 'key_points']):
                        logger.error(f"日程数据格式不完整: {schedule}")
                        return None
                    if schedule['importance'] not in ['high', 'medium', 'low']:
                        schedule['importance'] = 'medium'
                
                # 缓存结果
                self._save_to_cache(cache_key, result)
                return result
                
            except json.JSONDecodeError as e:
                logger.error(f"JSON解析失败: {str(e)}\n响应内容: {response}")
                return None
            
        except Exception as e:
            logger.error(f"内容分析失败: {str(e)}", exc_info=True)
            return None

    def generate_schedule(self, analysis: Dict) -> Optional[Dict]:
        """根据分析结果生成完整的日程信息"""
        try:
            # 检查缓存
            cache_key = self._get_cache_key('schedule', json.dumps(analysis))
            cached_result = self._get_from_cache(cache_key)
            if cached_result:
                return cached_result

            # 基于分析结果构建基础日程信息
            schedule = {
                'title': analysis.get('title'),
                'description': analysis.get('description', ''),
                'date': analysis.get('date'),
                'start_time': analysis.get('start_time'),
                'end_time': analysis.get('end_time'),
                'location': analysis.get('location', ''),
                'importance': analysis.get('importance', 'medium'),
                'period': 'morning' if int(analysis.get('start_time', '00:00').split(':')[0]) < 12 else 'afternoon',
                'preparations': [],
                'reminders': [
                    {
                        'time': f"{analysis.get('date')} {analysis.get('start_time')}",
                        'content': f"准备开始：{analysis.get('title')}",
                        'type': '开始'
                    }
                ],
                'follow_ups': [],
                'ai_suggestions': {
                    'time_management': '建议提前5-10分钟到达',
                    'key_focus': analysis.get('key_points', ['按时参加'])[0],
                    'risk_prevention': '确保时间准确，提前确认地点',
                    'efficiency_tips': '准备好相关材料，提高会议效率'
                },
                'risk_assessment': {
                    'level': 'medium',
                    'factors': ['时间安排冲突', '准备不充分'],
                    'mitigations': ['提前确认时间', '提前准备材料']
                }
            }

            # 根据关键点生成准备事项
            key_points = analysis.get('key_points', [])
            for point in key_points:
                schedule['preparations'].append({
                    'item': f"准备{point}相关材料",
                    'deadline': analysis.get('date'),
                    'priority': 'high'
                })

            # 添加提醒
            reminder_time = datetime.strptime(f"{analysis.get('date')} {analysis.get('start_time')}", '%Y-%m-%d %H:%M')
            reminder_time = reminder_time - timedelta(minutes=15)
            schedule['reminders'].append({
                'time': reminder_time.strftime('%Y-%m-%d %H:%M'),
                'content': f"即将开始：{analysis.get('title')}",
                'type': '提醒'
            })

            # 缓存结果
            self._save_to_cache(cache_key, schedule)
            return schedule
            
        except Exception as e:
            logger.error(f"生成日程失败: {str(e)}", exc_info=True)
            return None

    def get_best_time(self, date: datetime.date, events: List[Dict]) -> Optional[Dict]:
        """获取最佳时间建议"""
        try:
            # 构建更智能的时间建议逻辑
            events_str = "\n".join([
                f"- {e['start_time']}-{e['end_time']}: {e['title']} (重要程度: {e.get('importance', 'medium')})" 
                for e in events
            ])
            
            prompt = f"""
            任务：基于现有日程安排，推荐最佳时间段。

            日期：{date}
            现有日程：
            {events_str}

            工作时间规则：
            1. 上午：09:00-12:00
            2. 下午：13:00-18:00
            3. 避免午休时间：12:00-13:00
            4. 建议预留缓冲时间

            要求：
            1. 避免时间冲突
            2. 考虑日程密度分布
            3. 预留准备和过渡时间
            4. 考虑时间段效率特点

            输出格式(JSON)：
            {{
                "start_time": "HH:MM",
                "end_time": "HH:MM",
                "period": "morning/afternoon",
                "duration": "建议时长(分钟)",
                "reason": "建议理由",
                "efficiency_score": 0.95,  // 时间段效率评分
                "buffer_time": {{
                    "before": "建议预留前置时间(分钟)",
                    "after": "建议预留后置时间(分钟)"
                }}
            }}
            """
            
            response = self.ai_service.chat_completion(prompt)
            if not response:
                return None
                
            suggestion = self.ai_service.parse_json_response(response)
            if not self._validate_time_suggestion(suggestion):
                return None
                
            return suggestion
            
        except Exception as e:
            logger.error(f"获取最佳时间建议失败: {str(e)}", exc_info=True)
            return None

    def _validate_schedule(self, schedule: Dict) -> bool:
        """验证生成的日程格式"""
        try:
            required_fields = [
                'title', 'description', 'date', 'start_time', 'end_time',
                'period', 'importance'
            ]
            
            # 检查必填字段
            if not all(field in schedule for field in required_fields):
                return False
            
            # 验证时间格式
            datetime.strptime(schedule['date'], '%Y-%m-%d')
            datetime.strptime(schedule['start_time'], '%H:%M')
            datetime.strptime(schedule['end_time'], '%H:%M')
            
            # 验证数据类型和值
            validations = [
                schedule['period'] in ['morning', 'afternoon'],
                schedule['importance'] in ['high', 'medium', 'low'],
                isinstance(schedule.get('preparations', []), list),
                isinstance(schedule.get('reminders', []), list),
                isinstance(schedule.get('follow_ups', []), list),
                isinstance(schedule.get('ai_suggestions', {}), dict)
            ]
            
            return all(validations)
            
        except (ValueError, KeyError, TypeError):
            return False

    def _validate_content_analysis(self, analysis: Dict) -> bool:
        """验证内容分析结果格式"""
        required_fields = ['title', 'date', 'start_time', 'end_time', 'importance']
        if not all(field in analysis for field in required_fields):
            return False
            
        try:
            datetime.strptime(analysis['date'], '%Y-%m-%d')
            datetime.strptime(analysis['start_time'], '%H:%M')
            datetime.strptime(analysis['end_time'], '%H:%M')
            return (
                isinstance(analysis['title'], str) and
                analysis['importance'] in ['high', 'medium', 'low'] and
                isinstance(analysis.get('participants', []), list) and
                isinstance(analysis.get('key_points', []), list)
            )
        except ValueError:
            return False

    def _validate_time_suggestion(self, suggestion: Dict) -> bool:
        """验证时间建议格式"""
        required_fields = ['start_time', 'end_time', 'period', 'reason']
        if not all(field in suggestion for field in required_fields):
            return False
            
        try:
            datetime.strptime(suggestion['start_time'], '%H:%M')
            datetime.strptime(suggestion['end_time'], '%H:%M')
            return (
                suggestion['period'] in ['morning', 'afternoon'] and
                isinstance(suggestion['reason'], str)
            )
        except ValueError:
            return False

    def get_schedule_suggestions(self, title: str, description: str = '') -> Optional[Dict]:
        """获取日程建议"""
        try:
            # 检查缓存
            cache_key = self._get_cache_key('suggest', f"{title}:{description}")
            cached_result = self._get_from_cache(cache_key)
            if cached_result:
                return cached_result

            # 构建提示词
            prompt = f"""
            作为一个专业的日程助手，请为以下日程提供建议。

            日程信息：
            标题：{title}
            描述：{description}

            请严格按照以下JSON格式返回建议，确保所有字段都存在且格式正确：

            {{
                "time_suggestions": [
                    {{
                        "day": "Monday",
                        "time": "10:00-11:30",
                        "reason": "这个时间段效率最高"
                    }},
                    {{
                        "day": "Wednesday",
                        "time": "14:00-15:30",
                        "reason": "团队都有空"
                    }}
                ],
                "preparation_suggestions": [
                    "准备会议议程",
                    "收集相关材料",
                    "通知相关人员"
                ],
                "duration_suggestion": {{
                    "recommended": 90,
                    "minimum": 60,
                    "maximum": 120,
                    "breakdown": {{
                        "opening": 5,
                        "main_discussion": 70,
                        "qa": 10,
                        "summary": 5
                    }}
                }},
                "efficiency_tips": [
                    "提前5分钟到达",
                    "准备好会议材料",
                    "设置会议议程",
                    "控制讨论时间"
                ]
            }}

            注意：
            1. 所有字段必须存在
            2. 时间格式为 HH:MM
            3. 时长单位为分钟
            4. 建议要具体且实用
            """

            # 调用AI服务，使用较低的temperature以获得更规范的输出
            response = self.ai_service.chat_completion(prompt, temperature=0.3)
            if not response:
                return None

            # 解析结果
            result = self.ai_service.parse_json_response(response)
            if not result:
                return None

            # 验证结果格式
            if not self._validate_suggestions(result):
                logger.error("建议格式验证失败")
                return None

            # 缓存结果
            self._save_to_cache(cache_key, result)
            return result

        except Exception as e:
            logger.error(f"获取日程建议失败: {str(e)}", exc_info=True)
            return None

    def _validate_suggestions(self, suggestions: Dict) -> bool:
        """验证建议格式"""
        try:
            required_sections = [
                'time_suggestions',
                'preparation_suggestions',
                'duration_suggestion',
                'efficiency_tips'
            ]
            
            # 检查必要部分是否存在
            if not all(section in suggestions for section in required_sections):
                return False

            # 验证时间建议格式
            time_suggestions = suggestions['time_suggestions']
            if not isinstance(time_suggestions, list):
                return False
            for suggestion in time_suggestions:
                if not all(key in suggestion for key in ['day', 'time', 'reason']):
                    return False

            # 验证准备建议格式
            if not isinstance(suggestions['preparation_suggestions'], list):
                return False

            # 验证时长建议格式
            duration = suggestions['duration_suggestion']
            if not all(key in duration for key in ['recommended', 'minimum', 'maximum']):
                return False

            # 验证效率建议格式
            if not isinstance(suggestions['efficiency_tips'], list):
                return False

            return True

        except (KeyError, TypeError):
            return False

    async def optimize_schedule(
        self,
        schedules: List[Dict],
        user_patterns: Dict,
        optimization_goals: List[str]
    ) -> Dict:
        """优化日程安排
        
        Args:
            schedules: 现有日程列表
            user_patterns: 用户行为模式
            optimization_goals: 优化目标
        """
        try:
            prompt = f"""
优化以下日程安排：

现有日程：
{json.dumps(schedules, ensure_ascii=False)}

用户习惯：
{json.dumps(user_patterns, ensure_ascii=False)}

优化目标：
{', '.join(optimization_goals)}

请提供：
1. 时间调整建议
2. 效率优化建议
3. 风险预警
4. 准备建议
5. 替代方案
"""
            response = await self.ai_service.chat_completion(prompt)
            return self._parse_optimization(response)
            
        except Exception as e:
            logger.error(f"优化日程失败: {str(e)}")
            raise

    async def generate_schedule_insights(
        self,
        user_id: int,
        time_range: str = 'last_month'
    ) -> Dict:
        """生成日程洞察
        
        Args:
            user_id: 用户ID
            time_range: 时间范围
        """
        try:
            # 获取用户日程数据
            schedule_data = await self._get_user_schedules(user_id, time_range)
            
            prompt = f"""
分析用户的日程模式：

日程数据：
{json.dumps(schedule_data, ensure_ascii=False)}

请提供：
1. 时间利用模式
2. 高效时段识别
3. 常见延误原因
4. 改进建议
5. 习惯优化建议
"""
            response = await self.ai_service.chat_completion(prompt)
            return self._parse_insights(response)
            
        except Exception as e:
            logger.error(f"生成日程洞察失败: {str(e)}")
            raise
